import 'dart:async';
import 'package:flutter/foundation.dart';

mixin YkAudioUtilDelegate {
  Future init();

  Future dispose();

  Future<bool> play(String url, void Function(double duration, double position) progressCallBack, void Function() finishCallBack);

  Future<bool> end();

  Future<bool> resume();

  Future<bool> pause();

  Future seekTo(double progress);
}

class YkAudioUtil with ChangeNotifier {
  static YkAudioUtil? _instance;

  static YkAudioUtil get instance {
    _instance ??= YkAudioUtil._();
    return _instance!;
  }

  YkAudioUtil._();

  bool isPlaying = false;
  bool isPause = false;
  double duration = 0.0;
  double position = 0.0;
  String? currentPlayingAudioID;

  YkAudioUtilDelegate? _delegate;
  final Map<String, String> _recordMap = <String, String>{};

  Future init({required YkAudioUtilDelegate delegate}) {
    _delegate = delegate;
    isPlaying = false;
    isPause = false;
    notifyListeners();
    return delegate.init();
  }

  Future register({required String audioID, required String audioUrl}) async {
    if (audioID.isNotEmpty && audioUrl.isNotEmpty) {
      _recordMap.addAll({audioID: audioUrl});
    }
  }

  Future play({required String audioID, void Function(double duration, double position)? progressCallBack, void Function()? finishCallBack}) async {
    if (isPlaying) {
      await end();
    }
    if (!_recordMap.keys.contains(audioID)) {
      throw Exception(['did not register this audioID']);
    }

    if (_delegate == null) {
      throw Exception(["did not execute init()"]);
    }

    final url = _recordMap[audioID] ?? "";
    final result = await _delegate!.play(url, (duration, position) {
      /// 进度回调
      progressCallBack?.call(duration, position);
      this.duration = duration;
      this.position = position;
      notifyListeners();
    }, () async {
      await end();
      finishCallBack?.call();
    });
    if (result) {
      isPlaying = true;
      isPause = false;
      currentPlayingAudioID = audioID;
      notifyListeners();
    }
  }

  Future pause() async {
    if (_delegate == null) {
      throw Exception(["did not execute init()"]);
    }
    final result = await _delegate?.pause() ?? false;
    if (result) {
      isPause = true;
      notifyListeners();
    }
    return;
  }

  Future resume() async {
    if (_delegate == null) {
      throw Exception(["did not execute init()"]);
    }
    final result = await _delegate?.resume() ?? false;
    if (result) {
      isPause = false;
      notifyListeners();
    }
    return;
  }

  Future end() async {
    if (_delegate == null) {
      throw Exception(["did not execute init()"]);
    }
    final result = await _delegate?.end() ?? false;
    if (result) {
      isPlaying = false;
      currentPlayingAudioID = null;
      duration = 0;
      position = 0;
      notifyListeners();
    }
    return Future.value();
  }

  Future seekTo({required double progress}) async {
    if (!isPlaying) {
      return;
    }
    await _delegate?.seekTo(progress);
  }

  Future unInit() async {
    await _delegate?.dispose().then((value) {
      _delegate = null;
    });
  }

  @override
  void dispose() {
    unInit();
    super.dispose();
  }
}
